﻿using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Documents;
using System.Windows.Media;
using ZOIL.Framework.Core;
using ZOIL.Framework.Presentation;

namespace ZOIL.Library.Behaviors
{
    public enum AdornerPlacement
    {
        In = 1,
        Out = 2,
        CenterOnEdge = 3
    }

    public enum AdornerPosition
    {
        LeftEdge = 1,
        RightEdge = 2,
        TopEdge = 3,
        BottomEdge = 4,
        Center = 5,
        TopLeftCorner = 6,
        TopRightCorner = 7,
        BottomLeftCorner = 8,
        BottomRightcorner = 9
    }

    public class ZComponentAdorner : Adorner
    {
        private readonly List<AdornerChild> _logicalChildren;
        private readonly VisualCollection _visualChildren;

        public ZComponentAdorner(ZComponent targetElement) : base(targetElement)
        {
            _visualChildren = new VisualCollection(this);
            _logicalChildren = new List<AdornerChild>();

            // find parent-landscape to force rendering adorner when animation changes
            ZTreeHelper.TryFindParents<ZInformationLandscape>(targetElement).First().Animation.Changed += Animation_Changed;
        }

        public ZComponent AdornedComponent
        {
            get { return AdornedElement as ZComponent; }
        }

        protected override int VisualChildrenCount
        {
            get { return _visualChildren.Count; }
        }

        private void Animation_Changed(object sender, ZLandscapeAnimationEventArgs e)
        {
            //force rendering of adorner
            InvalidateVisual();
        }

        public void AddChild(FrameworkElement elem, AdornerPlacement placement, AdornerPosition pos)
        {
            _logicalChildren.Add(new AdornerChild(elem, placement, pos));
            _visualChildren.Add(elem);
            InvalidateArrange();
        }

        public void AddChild(FrameworkElement elem, AdornerPlacement placement, AdornerPosition pos, double marginTop, double marginLeft)
        {
            _logicalChildren.Add(new AdornerChild(elem, placement, pos, marginTop, marginLeft));
            _visualChildren.Add(elem);
            InvalidateArrange();
        }

        private Point getAdornerChildPosition(AdornerChild child)
        {
            var adorningPoint = new Point();

            //standard placement is IN

            switch (child.AdornerPosition)
            {
                case AdornerPosition.LeftEdge:
                    adorningPoint.X = 0;
                    adorningPoint.Y = 0;

                    if (child.AdornerPlacement == AdornerPlacement.Out)
                    {
                        adorningPoint.X -= child.AdorningElement.Width;
                    }
                    else if (child.AdornerPlacement == AdornerPlacement.CenterOnEdge)
                    {
                        adorningPoint.X -= (child.AdorningElement.Width/2.0);
                    }

                    break;
                case AdornerPosition.RightEdge:
                    adorningPoint.X = AdornedComponent.Width - child.AdorningElement.Width;
                    adorningPoint.Y = 0;

                    if (child.AdornerPlacement == AdornerPlacement.Out)
                    {
                        adorningPoint.X += child.AdorningElement.Width;
                    }
                    else if (child.AdornerPlacement == AdornerPlacement.CenterOnEdge)
                    {
                        adorningPoint.X += (child.AdorningElement.Width/2.0);
                    }

                    break;
                case AdornerPosition.TopEdge:
                    adorningPoint.X = 0;
                    adorningPoint.Y = 0;

                    if (child.AdornerPlacement == AdornerPlacement.Out)
                    {
                        adorningPoint.Y -= child.AdorningElement.Height;
                    }
                    else if (child.AdornerPlacement == AdornerPlacement.CenterOnEdge)
                    {
                        adorningPoint.Y -= (child.AdorningElement.Height/2.0);
                    }

                    break;
                case AdornerPosition.BottomEdge:
                    adorningPoint.X = 0;
                    adorningPoint.Y = AdornedComponent.Height - child.AdorningElement.Height;

                    if (child.AdornerPlacement == AdornerPlacement.Out)
                    {
                        adorningPoint.Y += child.AdorningElement.Height;
                    }
                    else if (child.AdornerPlacement == AdornerPlacement.CenterOnEdge)
                    {
                        adorningPoint.Y += (child.AdorningElement.Height/2.0);
                    }

                    break;
                case AdornerPosition.Center:

                    //center does ignore placement and reference values
                    adorningPoint.X = AdornedComponent.Width - (child.AdorningElement.Width/2.0);
                    adorningPoint.Y = AdornedComponent.Height - (child.AdorningElement.Height/2.0);
                    break;
                case AdornerPosition.TopLeftCorner:
                    adorningPoint.X = 0;
                    adorningPoint.Y = 0;

                    if (child.AdornerPlacement == AdornerPlacement.Out)
                    {
                        adorningPoint.X -= child.AdorningElement.Width;
                        adorningPoint.Y -= child.AdorningElement.Height;
                    }
                    else if (child.AdornerPlacement == AdornerPlacement.CenterOnEdge)
                    {
                        adorningPoint.X -= child.AdorningElement.Width/2.0;
                        adorningPoint.Y -= child.AdorningElement.Height/2.0;
                    }
                    break;
                case AdornerPosition.TopRightCorner:
                    adorningPoint.X = AdornedComponent.Width - child.AdorningElement.Width;
                    adorningPoint.Y = 0;

                    if (child.AdornerPlacement == AdornerPlacement.Out)
                    {
                        adorningPoint.X += child.AdorningElement.Width;
                        adorningPoint.Y -= child.AdorningElement.Height;
                    }
                    else if (child.AdornerPlacement == AdornerPlacement.CenterOnEdge)
                    {
                        adorningPoint.X += child.AdorningElement.Width/2.0;
                        adorningPoint.Y -= child.AdorningElement.Height/2.0;
                    }
                    break;
                case AdornerPosition.BottomLeftCorner:
                    adorningPoint.X = 0;
                    adorningPoint.Y = AdornedComponent.Height - child.AdorningElement.Height;

                    if (child.AdornerPlacement == AdornerPlacement.Out)
                    {
                        adorningPoint.X -= child.AdorningElement.Width;
                        adorningPoint.Y += child.AdorningElement.Height;
                    }
                    else if (child.AdornerPlacement == AdornerPlacement.CenterOnEdge)
                    {
                        adorningPoint.X -= child.AdorningElement.Width/2.0;
                        adorningPoint.Y += child.AdorningElement.Height/2.0;
                    }
                    break;
                case AdornerPosition.BottomRightcorner:
                    adorningPoint.X = AdornedComponent.Width - child.AdorningElement.Width;
                    ;
                    adorningPoint.Y = AdornedComponent.Height - child.AdorningElement.Height;

                    if (child.AdornerPlacement == AdornerPlacement.Out)
                    {
                        adorningPoint.X += child.AdorningElement.Width;
                        adorningPoint.Y += child.AdorningElement.Height;
                    }
                    else if (child.AdornerPlacement == AdornerPlacement.CenterOnEdge)
                    {
                        adorningPoint.X += child.AdorningElement.Width/2.0;
                        adorningPoint.Y += child.AdorningElement.Height/2.0;
                    }
                    break;
                default:
                    break;
            }

            adorningPoint.X += child.MarginLeft;
            adorningPoint.Y += child.MarginTop;

            //int pos = (int)child.AdornerPosition - 1;

            //Rect adorneredElementRect = new Rect(0, 0, AdornedComponent.Width, AdornedComponent.Height);
            //Rect adorningElementRect = new Rect(0, 0, child.AdorningElement.Width, child.AdorningElement.Height);

            //adorningPoint.X = (((pos % 3) / 2d) * adorneredElementRect.Width) - (((pos % 3) / 2d) * adorningElementRect.Width);
            //adorningPoint.Y = (((pos / 3) / 2d) * adorneredElementRect.Height) - (((pos / 3) / 2d) * adorningElementRect.Height);

            return adorningPoint;
        }

        protected override Size ArrangeOverride(Size finalSize)
        {
            foreach (AdornerChild child in _logicalChildren)
            {
                Point adorningPoint = getAdornerChildPosition(child);
                child.AdorningElement.Arrange(new Rect(adorningPoint, child.AdorningElement.DesiredSize));
            }
            return finalSize;
        }

        protected override Visual GetVisualChild(int index)
        {
            return _visualChildren[index];
        }

        private class AdornerChild
        {
            public AdornerChild(FrameworkElement elem, AdornerPlacement placement, AdornerPosition pos) : this(elem, placement, pos, 0.0, 0.0)
            {
            }

            public AdornerChild(FrameworkElement elem, AdornerPlacement placement, AdornerPosition pos, double marginTop, double marginLeft)
            {
                AdorningElement = elem;
                AdornerPlacement = placement;
                AdornerPosition = pos;
                MarginLeft = marginLeft;
                MarginTop = marginTop;
            }

            public FrameworkElement AdorningElement { get; set; }
            public AdornerPlacement AdornerPlacement { get; set; }
            public AdornerPosition AdornerPosition { get; set; }
            public double MarginTop { get; set; }
            public double MarginLeft { get; set; }
        }
    }
}